#include "Shader.h"

#include "../exceptions/ShaderError.h"

void Shader::loadFile(const char* fn, std::string& str) {
	std::ifstream in(fn);
	if (!in.is_open()) {
		std::cout << "The file " << fn << " cannot be opened\n";
		return;
	}
	char tmp[300];
	while (!in.eof()) {
		in.getline(tmp, 300);
		str += tmp;
		str += '\n';
	}
}

unsigned int Shader::loadShader(const char * shader_file, GLenum shaderType) {

	GLuint shader;
	std::string logstr;
	shader = glCreateShader(shaderType);
	if (shader == 0) {
		// Throw
		throw ShaderError("glCreateShader:Error creating shader form file %s\n", shader_file);
	}

	// cargo el archivo de shaders en memoria
	std::ifstream v_shader_file(shader_file, std::ifstream::in);
	std::string v_str((std::istreambuf_iterator<char>(v_shader_file)), std::istreambuf_iterator<char>());
	const char* vs_code_array[] = { v_str.c_str() };

	glShaderSource(shader, 1, vs_code_array, NULL);

	// Compilar el shader
	glCompileShader(shader);

	// verificar resultado de la compilacion
	GLint vs_compilation_result;

	glGetShaderiv(shader, GL_COMPILE_STATUS, &vs_compilation_result);

	if (vs_compilation_result == GL_FALSE) {
		GLint logLen;
		glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLen);
		if (logLen > 0) {
			char * log = (char *) malloc(logLen);
			GLsizei written;
			glGetShaderInfoLog(shader, logLen, &written, log);
			logstr.assign(log);
			free(log);
			std::cout << logstr << std::endl;
			throw ShaderError("Vertex shader compilation failed in file %s\n log: %s\n", shader_file,
					logstr.c_str());
		}
		throw ShaderError("Vertex shader compilation failed in file %s\n\n", shader_file);
	}
	return shader;
}

Shader::Shader(const char* vertex_shader, const char* fragment_shader) {

	std::string logstr;
	//loadFile(vertex_shader, source);
	vs = loadShader(vertex_shader, GL_VERTEX_SHADER);
	//source = "";
	//loadFile(fragment_shader, source);
	fs = loadShader(fragment_shader, GL_FRAGMENT_SHADER);

	program = glCreateProgram();
	glAttachShader(program, vs);
	glAttachShader(program, fs);

	glLinkProgram(program);

	GLint status;
	glGetProgramiv(program, GL_LINK_STATUS, &status); // Error Check
	if (GL_FALSE == status) {
		GLint logLen;
		glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLen);
		if (logLen > 0) {
			char * log = (char *) malloc(logLen);
			GLsizei written;
			glGetProgramInfoLog(program, logLen, &written, log); // Get link Info Error
			logstr.assign(log);
			free(log);
			throw ShaderError("%s\n log:%s \n", "Failed to link shader program!", logstr.c_str());
		}
		throw ShaderError("%s %s and %s\n", "Failed to link shader program for", vertex_shader,
				fragment_shader);
	}
	glUseProgram(program);
}

Shader::~Shader() {
	glDetachShader(program, vs);
	glDetachShader(program, fs);
	glDeleteShader(vs);
	glDeleteShader(fs);
	glDeleteProgram(program);
}

void Shader::use_shader() {
	glUseProgram(program);
}

unsigned int Shader::get_program() {
	return program;
}

void Shader::remove_shader() {
	glUseProgram(0);
}

